package com.esri.arcgis.android.samples.nearby;

import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RatingBar;
import android.widget.TextView;
import android.widget.Toast;

import com.esri.android.map.GraphicsLayer;
import com.esri.android.map.LocationDisplayManager;
import com.esri.android.map.MapView;
import com.esri.android.map.event.OnSingleTapListener;
import com.esri.android.map.event.OnStatusChangedListener;
import com.esri.core.geometry.Envelope;
import com.esri.core.geometry.GeometryEngine;
import com.esri.core.geometry.LinearUnit;
import com.esri.core.geometry.MultiPoint;
import com.esri.core.geometry.Point;
import com.esri.core.geometry.SpatialReference;
import com.esri.core.geometry.Unit;
import com.esri.core.map.CallbackListener;
import com.esri.core.map.Graphic;
import com.esri.core.symbol.PictureMarkerSymbol;
import com.esri.core.symbol.Symbol;
import com.esri.core.tasks.geocode.Locator;
import com.esri.core.tasks.geocode.LocatorFindParameters;
import com.esri.core.tasks.geocode.LocatorGeocodeResult;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;


public class Nearby extends AppCompatActivity {

    private static final String TAG = Nearby.class.getSimpleName();

    // Define types of search.
    private enum SearchType {
        BAR,
        PIZZA,
        COFFEE,
    }

    SearchType          mCurrentSearchType;
    final static double ZOOM_BY = 20;
    LinearUnit          mMilesUnit = new LinearUnit(LinearUnit.Code.MILE_STATUTE);

    ProgressBar         mProgress;
    MapView             mMapView = null;
    SpatialReference    mMapSr = null;
    GraphicsLayer       mResultsLayer = null;
    PictureMarkerSymbol mCoffeeMapIcon, mBarMapIcon, mPizzaMapIcon;

    // Views to show selected search result information.
    TextView            mTitleTextView;
    TextView            mAddressTextView;
    TextView            mPhoneTextView;
    ImageView           mPhoneImageView;
    TextView            mDistanceTextView;

    RatingBar           mRatingBar;

    Locator mLocator;
    ArrayList<String>   mFindOutFields = new ArrayList<>();

    LocationDisplayManager locationDisplayManager;


    /**
     * When the map is tapped, select the graphic at that location.
     */
    final OnSingleTapListener mapTapCallback = new OnSingleTapListener() {

        @Override
        public void onSingleTap(float x, float y) {
            // Find out if we tapped on a Graphic
            int[] graphicIDs = mResultsLayer.getGraphicIDs(x, y, 25);
            if (graphicIDs != null && graphicIDs.length > 0) {
                // If there is more than one graphic, only select the first found.
                if (graphicIDs.length > 1) {
                    int id = graphicIDs[0];
                    graphicIDs = new int[]{id};
                }

                mResultsLayer.clearSelection();
                mResultsLayer.setSelectedGraphics(graphicIDs, true);

                Graphic gr = mResultsLayer.getGraphic(graphicIDs[0]);
                updateContent(gr.getAttributes());
            }
        }
    };


    final OnStatusChangedListener statusChangedListener = new OnStatusChangedListener() {
        private static final long serialVersionUID = 1L;
        @Override
        public void onStatusChanged(Object source, STATUS status) {

            // 地图初始化成功
            if (source == mMapView && status == STATUS.INITIALIZED) {
                mMapSr = mMapView.getSpatialReference();
                if (locationDisplayManager == null) {
                    setupLocationListener();
                }
            }
        }
    };

    /**
     * When user touches phone number, send this to the dialler using an intent.
     */
    final View.OnTouchListener callTouchListener = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            String num = mPhoneTextView.getText().toString();
            Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + num));
            startActivity(intent);
            return true;
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mMapView = (MapView) findViewById(R.id.map);
        mMapView.setOnStatusChangedListener(statusChangedListener);
        mMapView.setOnSingleTapListener(mapTapCallback);

        mTitleTextView      = (TextView) findViewById(R.id.titleTextView);
        mTitleTextView.setText(R.string.startup_caption);
        mAddressTextView    = (TextView) findViewById(R.id.addressTextView);
        mPhoneTextView      = (TextView) findViewById(R.id.phoneTextView);
        mPhoneTextView.setOnTouchListener(callTouchListener);
        mPhoneImageView     = (ImageView) findViewById(R.id.callImageView);
        mPhoneImageView.setOnTouchListener(callTouchListener);
        mDistanceTextView   = (TextView) findViewById(R.id.distanceTextView);
        mRatingBar          = (RatingBar) findViewById(R.id.ratingBar);
        mProgress           = (ProgressBar) findViewById(R.id.findProgress);

        mResultsLayer       = new GraphicsLayer();
        mResultsLayer.setSelectionColorWidth(6);
        mMapView.addLayer(mResultsLayer);

        mCoffeeMapIcon  = new PictureMarkerSymbol(getApplicationContext(),  ContextCompat.getDrawable(getApplicationContext(), R.drawable.ic_local_cafe_black));
        mPizzaMapIcon   = new PictureMarkerSymbol(getApplicationContext(),  ContextCompat.getDrawable(getApplicationContext(), R.drawable.ic_local_pizza_black));
        mBarMapIcon     = new PictureMarkerSymbol(getApplicationContext(),  ContextCompat.getDrawable(getApplicationContext(), R.drawable.ic_local_drink_black));

        setupLocator();
        setupLocationListener();
    }

    private void setupLocator() {
        // Parameterless constructor - uses the Esri world geocoding service.
        mLocator = Locator.createOnlineLocator();

        // Set up the outFields parameter for the search.
        mFindOutFields.add(getResources().getString(R.string.result_title));
        mFindOutFields.add(getResources().getString(R.string.result_type));
        mFindOutFields.add(getResources().getString(R.string.result_address));
        mFindOutFields.add(getResources().getString(R.string.result_phone));
        mFindOutFields.add(getResources().getString(R.string.result_distance));
    }

    // 自定定位
    private void setupLocationListener() {
        if ((mMapView != null) && (mMapView.isLoaded())) {
            locationDisplayManager = mMapView.getLocationDisplayManager();
            locationDisplayManager.setLocationListener(new LocationListener() {

                boolean locationChanged = false;

                @Override
                public void onLocationChanged(Location loc) {
                    if (!locationChanged) {
                        locationChanged = true;
                        // 缩放到当前位置
                        zoomToLocation(loc);
                        // 设置 定位模式
                        locationDisplayManager.setAutoPanMode(LocationDisplayManager.AutoPanMode.LOCATION);
                    }
                }

                @Override
                public void onProviderDisabled(String arg0) {
                }

                @Override
                public void onProviderEnabled(String arg0) {
                }

                @Override
                public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
                }
            });

            locationDisplayManager.start();
        }
    }

    // 缩放到 当前位置
    private void zoomToLocation(Location loc) {
        Point mapPoint      = getAsPoint(loc);
        Unit mapUnit        = mMapSr.getUnit();
        double zoomFactor   = Unit.convertUnits(ZOOM_BY, Unit.create(LinearUnit.Code.MILE_US), mapUnit);
        Envelope zoomExtent = new Envelope(mapPoint, zoomFactor, zoomFactor);
        mMapView.setExtent(zoomExtent);
    }

    /** Performs a find using the Locator, for a specific type of business. **/
    private void doFindNearbyAsync(String searchFor) {

        // 初始化 findCallback
        final CallbackListener<List<LocatorGeocodeResult>> findCallback = new CallbackListener<List<LocatorGeocodeResult>>() {

                    @Override
                    public void onError(Throwable e) {
                        setProgressOnUIThread(false);
                        showToastOnUiThread("Error searching for results");
                    }

                    @Override
                    public void onCallback(List<LocatorGeocodeResult> results) {
                        // 回调结果
                        mResultsLayer.removeAll();

                        // Use a Multipoint as a simple way to set total extent.
                        MultiPoint fullExtent = new MultiPoint();

                        if (results.size() > 0) {
                            // Set specific symbols and selection color for each type of search.
                            Symbol symbol = null;

                            if (mCurrentSearchType        == SearchType.BAR) {
                                mResultsLayer.setSelectionColor(getResources().getColor(R.color.beer_selection));
                                symbol = mBarMapIcon;
                            } else if (mCurrentSearchType == SearchType.PIZZA) {
                                mResultsLayer.setSelectionColor(getResources().getColor(R.color.pizza_selection));
                                symbol = mPizzaMapIcon;
                            } else if (mCurrentSearchType == SearchType.COFFEE) {
                                mResultsLayer.setSelectionColor(getResources().getColor(R.color.coffee_selection));
                                symbol = mCoffeeMapIcon;
                            }

                            for (LocatorGeocodeResult result : results) {
                                Point resultPoint               = result.getLocation();
                                HashMap<String, Object> attrMap = new HashMap<String, Object>(result.getAttributes());
                                mResultsLayer.addGraphic(new Graphic(resultPoint, symbol, attrMap));

                                fullExtent.add(resultPoint);
                            }
                            // Zoom to the full extent
                            mMapView.setExtent(fullExtent, 100);
                        }
                        // Update the UI with the result information.
                        setResultCount(results.size(), mCurrentSearchType);
                    }
                };

        try {
            setProgressOnUIThread(true);

            // Get the current map extent.
            Envelope currExt = new Envelope();
            mMapView.getExtent().queryEnvelope(currExt);

            // 设置查询的参数
            LocatorFindParameters fParams = new LocatorFindParameters(searchFor);
            // 设置 查询范围：当前手机屏幕
            fParams.setSearchExtent(currExt, mMapSr);
            // 设置 输入SR
            fParams.setOutSR(mMapSr);
            fParams.setOutFields(mFindOutFields);
            // 设置 当前位置信息
            if ((locationDisplayManager != null) && (locationDisplayManager.getLocation() != null)) {
                Point currentPoint = getAsPoint(locationDisplayManager.getLocation());
                fParams.setLocation(currentPoint, mMapSr);
            }
            // 执行查询
            mLocator.find(fParams, findCallback);
        } catch (Exception e) {
            setProgressOnUIThread(false);
            showToastOnUiThread("Error searching for results");
        }
    }

    private static String getRating() {
        // Randomized ratings could be replaced by a ratings service from a third party.
        Random r = new Random();
        return String.valueOf(1 + (r.nextFloat() * 4));
    }


    private void setResultCount(int resultCount, SearchType searchType) {
        String searchTypeMessage = "";

        switch (searchType) {
            case COFFEE:
                searchTypeMessage = getResources().getString(R.string.results_coffee);
                break;
            case PIZZA:
                searchTypeMessage = getResources().getString(R.string.results_pizza);
                break;
            case BAR:
                searchTypeMessage = getResources().getString(R.string.results_bar);
                break;
        }

        final String message = String.format("Found %d %s", resultCount, searchTypeMessage);

        runOnUiThread(new Runnable() {
            public void run() {
                mProgress           .setIndeterminate(false);
                mTitleTextView      .setText(message);
                mAddressTextView    .setText("");
                mPhoneTextView      .setText("");
                mPhoneImageView     .setImageDrawable(null);
                mDistanceTextView   .setText("");
                mRatingBar          .setVisibility(View.GONE);
            }
        });
    }


    // 菜单栏
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_layout, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            // 酒吧
            case R.id.bar:
                if (mMapView.isLoaded()) {
                    clearCurrentResults();
                    mCurrentSearchType = SearchType.BAR;
                    try {
                        doFindNearbyAsync(getResources().getString(R.string.bar_query));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
                return true;
            // 披萨
            case R.id.pizza:
                if (mMapView.isLoaded()) {
                    mCurrentSearchType = SearchType.PIZZA;
                    try {
                        doFindNearbyAsync(getResources().getString(R.string.pizza_query));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                return true;
            // 咖啡馆
            case R.id.coffee:
                if (mMapView.isLoaded()) {
                    mCurrentSearchType = SearchType.COFFEE;
                    try {
                        doFindNearbyAsync(getResources().getString(R.string.coffee_query));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }

                return true;
            // 位置刷新
            case R.id.locate:
                if (mMapView.isLoaded()) {

                    if ((locationDisplayManager != null) && (locationDisplayManager.getLocation() != null)) {
                        // Keep current scale and go to current location, if there is one.
                        locationDisplayManager.setAutoPanMode(LocationDisplayManager.AutoPanMode.LOCATION);
                    }
                }

                return true;

            default:
                return super.onOptionsItemSelected(item);

        }
    }

    // 位置 转 点
    private Point getAsPoint(Location loc) {
        Point wgsPoint = new Point(loc.getLongitude(), loc.getLatitude());
        return (Point) GeometryEngine.project(wgsPoint, SpatialReference.create(4326), mMapSr);
    }

    // 更新显示内容
    public void updateContent(Map<String, Object> attributes) {

        String title    = attributes.get(getResources().getString(R.string.result_title)).toString();
        mTitleTextView.setText(title);

        String address  = attributes.get(getResources().getString(R.string.result_address)).toString();
        mAddressTextView.setText(address);

        String distance = attributes.get(getResources().getString(R.string.result_distance)).toString();

        double meters = Double.parseDouble(distance);
        if (meters > 0) {
            if (mDistanceTextView.getVisibility() != View.VISIBLE) {
                mDistanceTextView.setVisibility(View.VISIBLE);
            }
            double miles = mMilesUnit.convertFromMeters(meters);
            mDistanceTextView.setText(String.format("%.2f %s", miles, getResources().getString(R.string.miles)));
        } else {
            mDistanceTextView.setVisibility(View.GONE);
        }

        String phone = attributes.get(getResources().getString(R.string.result_phone)).toString();
        mPhoneTextView.setText(phone);
        mPhoneImageView.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(), R.drawable.ic_action_call));

        Float rating = Float.parseFloat(getRating());
        mRatingBar.setRating(rating);
        mRatingBar.setVisibility(View.VISIBLE);
    }

    private void clearCurrentResults() {
        if (mResultsLayer != null) {
            mResultsLayer.removeAll();
        }
        mTitleTextView      .setText("");
        mAddressTextView    .setText("");
        mPhoneTextView      .setText("");
        mPhoneImageView     .setImageDrawable(null);
        mDistanceTextView   .setText("");
        mRatingBar          .setRating(0);
        mRatingBar          .setVisibility(View.GONE);
    }

    public void showToastOnUiThread(final String message) {

        runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(Nearby.this, message, Toast.LENGTH_SHORT).show();
            }
        });
    }

    public void setProgressOnUIThread(final boolean isIndeterminate) {
        runOnUiThread(new Runnable() {
            public void run() {
                mProgress.setIndeterminate(isIndeterminate);
            }
        });
    }

    @Override
    protected void onPause() {
        super.onPause();
        mMapView.pause();
        if (locationDisplayManager != null) {
            locationDisplayManager.pause();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        mMapView.unpause();
        if (locationDisplayManager != null) {
            locationDisplayManager.resume();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (locationDisplayManager != null) {
            locationDisplayManager.stop();
        }
    }


}